﻿// 2023/3/8: 首个版本
// 2023/3/20: 修正caller调用
// 2023/4/18: 按键移动至底部。窗口可最大化
// 2023/9/22: 使用PassParent给ShowDialog传参
// 2023/9/25: 改为直接输入Icon对象

using System;
using System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Text;
using Eto.Forms;
using Eto.Drawing;
using ASEva;
using ASEva.Utility;
using ASEva.Samples;
using ASEva.UIEto;

namespace CommonSource
{
    partial class ConfigEditor : DialogPanel
    {
        public ConfigEditor(object caller, Icon icon = null)
        {
            this.caller = caller;

            Icon = icon;
            Title = chinese ? "配置编辑器" : "Config Editor";
            SetResizableMode(1000, 600, 1000, 600);

            var mainLayout = this.SetContentAsRowLayout(8, 8, VerticalAlignment.Stretch);
            var leftCol = mainLayout.AddColumnLayout(false, 200, 0, 8);
            var rightCol = mainLayout.AddColumnLayout(true);

            var entrySearch = leftCol.AddControl(new SearchBox()) as SearchBox;
            list = leftCol.AddControl(new ListBox(), true) as ListBox;

            var statusLayout = rightCol.AddTableLayout(false, 8, 2);

            var statusRow1 = statusLayout.AddRow();
            statusRow1.AddLabel(chinese ? "主状态:" : "Main status:");
            var labelMainStatusValue = statusRow1.AddLabel("-", true);

            var statusRow2 = statusLayout.AddRow();
            statusRow2.AddLabel(chinese ? "子状态:" : "Child status:");
            var labelChildStatusValue = statusRow2.AddLabel("-", true);

            var text = rightCol.AddControl(new TextArea(), true) as TextArea;
            text.Enabled = false;

            var buttonsLayout = rightCol.AddRowLayout();
            var buttonImport = buttonsLayout.AddButton(chinese ? "导入" : "Import", false, 80);
            var buttonExport = buttonsLayout.AddButton(chinese ? "导出" : "Export", false, 80);
            var buttonDisable = buttonsLayout.AddButton(chinese ? "禁用" : "Disable", false, 80);
            buttonsLayout.AddSpace();
            var buttonXml = buttonsLayout.AddButton(chinese ? "打开XML编辑器" : "Edit as XML", false, 100);
            var buttonEdit = buttonsLayout.AddButton(chinese ? "编辑" : "Edit", false, 80);
            var buttonSave = buttonsLayout.AddButton(chinese ? "保存" : "Save", false, 80);
            buttonImport.Enabled = buttonExport.Enabled = buttonDisable.Enabled = buttonXml.Enabled = buttonEdit.Enabled = buttonSave.Enabled = false;

            updateList(null);

            entrySearch.TextChanged += delegate
            {
                updateList(entrySearch.Text);
            };

            list.SelectedIndexChanged += delegate
            {
                if (list.SelectedIndex < 0)
                {
                    labelMainStatusValue.Text = "-";
                    labelMainStatusValue.TextColor = Colors.Black;
                    labelChildStatusValue.Text = "-";
                    buttonEdit.Enabled = false;
                    buttonSave.Enabled = false;
                    buttonDisable.Enabled = false;
                    buttonXml.Enabled = false;
                    buttonImport.Enabled = false;
                    buttonExport.Enabled = false;
                    text.Enabled = false;
                    text.Text = "";
                }
                else
                {
                    var id = list.SelectedKey;
                    Color textColor;
                    labelMainStatusValue.Text = getMainStatusText(id, out textColor);
                    labelMainStatusValue.TextColor = textColor;
                    labelChildStatusValue.Text = getChildStatusText(id);
                    buttonEdit.Enabled = true;
                    buttonSave.Enabled = false;
                    buttonDisable.Enabled = Agency.GetModuleConfigStatus(caller, id) != ConfigStatus.Disabled;
                    buttonImport.Enabled = true;
                    buttonExport.Enabled = true;
                    text.Enabled = false;
                    updateTextAndXmlButton(id, text, buttonXml);
                }
            };

            buttonEdit.Click += delegate
            {
                buttonEdit.Enabled = false;
                buttonSave.Enabled = true;
                buttonDisable.Enabled = false;
                buttonXml.Enabled = false;
                buttonImport.Enabled = false;
                buttonExport.Enabled = false;
                text.Enabled = true;
                list.Enabled = false;
                entrySearch.Enabled = false;  
            };

            buttonSave.Click += delegate
            {
                buttonEdit.Enabled = true;
                buttonSave.Enabled = false;
                buttonImport.Enabled = true;
                buttonExport.Enabled = true;
                text.Enabled = false;
                list.Enabled = true;
                entrySearch.Enabled = true;

                var id = list.SelectedKey;
                Agency.SetModuleConfig(caller, id, text.Text);
                Color textColor;
                labelMainStatusValue.Text = getMainStatusText(id, out textColor);
                labelMainStatusValue.TextColor = textColor;
                labelChildStatusValue.Text = getChildStatusText(id);
                updateTextAndXmlButton(id, text, buttonXml);

                buttonDisable.Enabled = Agency.GetModuleConfigStatus(caller, id) != ConfigStatus.Disabled;
            };

            buttonDisable.Click += delegate
            {
                var id = list.SelectedKey;
                Agency.DisableModule(caller, id);
                
                Color textColor;
                labelMainStatusValue.Text = getMainStatusText(id, out textColor);
                labelMainStatusValue.TextColor = textColor;
                labelChildStatusValue.Text = getChildStatusText(id);
                updateTextAndXmlButton(id, text, buttonXml);

                buttonDisable.Enabled = Agency.GetModuleConfigStatus(caller, id) != ConfigStatus.Disabled;
            };

            buttonXml.Click += delegate
            {
                var dialog = new XmlEditor(text.Text, icon);
                App.RunDialog(dialog);
                if (dialog.BoolResult)
                {
                    var id = list.SelectedKey;
                    Agency.SetModuleConfig(caller, id, dialog.XmlString);
                    Color textColor;
                    labelMainStatusValue.Text = getMainStatusText(id, out textColor);
                    labelMainStatusValue.TextColor = textColor;
                    labelChildStatusValue.Text = getChildStatusText(id);
                    updateTextAndXmlButton(id, text, buttonXml);
                }
            };

            buttonImport.Click += delegate
            {
                var dialog = new OpenFileDialog();
                dialog.Filters.Add(new FileFilter(chinese ? "模块配置文件 (*.asmc)" : "Module Config (*.asmc)", ".asmc"));
                if (dialog.ShowDialog(App.PassParent(this)) == DialogResult.Ok)
                {
                    var id = list.SelectedKey;
                    importConfig(dialog.FileName, id);
                    Color textColor;
                    labelMainStatusValue.Text = getMainStatusText(id, out textColor);
                    labelMainStatusValue.TextColor = textColor;
                    labelChildStatusValue.Text = getChildStatusText(id);
                    updateTextAndXmlButton(id, text, buttonXml);
                }
            };

            buttonExport.Click += delegate
            {
                var id = list.SelectedKey;
                var dialog = new SaveFileDialog();
                dialog.Filters.Add(new FileFilter(chinese ? "模块配置文件 (*.asmc)" : "Module Config (*.asmc)", ".asmc"));
                dialog.FileName = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + Path.DirectorySeparatorChar + list.SelectedValue + ".asmc";
                if (dialog.ShowDialog(App.PassParent(this)) == DialogResult.Ok)
                {
                    exportConfig(dialog.FileName, id);
                }
            };
        }

        private void updateTextAndXmlButton(String id, TextArea text, Button buttonXml)
        {
            var configString = Agency.GetModuleConfig(caller, id);
            if (configString == null) configString = "";

            String niceXmlString;
            bool isXml = checkIsXml(configString, out niceXmlString);

            buttonXml.Enabled = isXml;
            text.Text = isXml ? niceXmlString : configString;
        }

        private void updateList(String key)
        {
            var items = genItems(key);

            list.Items.Clear();
            foreach (var item in items) list.Items.Add(item.Name, item.ID);
        }

        private List<ConfigEditorItem> genItems(String key)
        {
            var fullList = new List<ConfigEditorItem>();
            fullList.Add(new ConfigEditorItem() { ID = "bus.system", Name = chinese ? "总线配置" : "Bus configuration"});
            fullList.Add(new ConfigEditorItem() { ID = "video.system", Name = chinese ? "视频配置" : "Video configuration"});
            fullList.Add(new ConfigEditorItem() { ID = "audio.system", Name = chinese ? "音频配置" : "Audio configuration"});
            fullList.Add(new ConfigEditorItem() { ID = "event.system", Name = chinese ? "事件配置" : "Event configuration"});
            foreach (var pair in Agency.GetProcessorClassTable())
            {
                fullList.Add(new ConfigEditorItem() { ID = pair.Key, Name = pair.Value.Title });
            }
            foreach (var pair in Agency.GetNativeClassTable())
            {
                fullList.Add(new ConfigEditorItem() { ID = pair.Key, Name = pair.Value.Title });
            }
            foreach (var pair in Agency.GetDeviceClassTable())
            {
                fullList.Add(new ConfigEditorItem() { ID = pair.Key, Name = pair.Value.Title });
            }
            
            key = key == null ? "" : key.ToLower();
            var filterList = new List<ConfigEditorItem>();
            foreach (var item in fullList)
            {
                if (key.Length == 0) filterList.Add(item);
                else
                {
                    if (item.Name.ToLower().Contains(key)) filterList.Add(item);
                }
            }

            filterList.Sort();
            return filterList;
        }

        private String getMainStatusText(String id, out Color color)
        {
            color = Colors.Black;
            var status = Agency.GetModuleConfigStatus(caller, id);
            switch (status)
            {
                case ConfigStatus.Disabled:
                    color = Colors.DimGray;
                    return chinese ? "未启用" : "Disabled";
                case ConfigStatus.Enabled:
                    color = Colors.Green;
                    return chinese ? "正常" : "OK";
                case ConfigStatus.EnabledWithError:
                    color = Colors.Red;
                    return chinese ? "错误" : "Error";
                case ConfigStatus.EnabledWithWarning:
                    color = Colors.DarkOrange;
                    return chinese ? "警告" : "Warning";
                default:
                    return chinese ? "未知" : "Unknown";
            }
        }

        private String getChildStatusText(String id)
        {
            var status = Agency.GetModuleChildConfigStatus(caller, id);
            if (status == null || status.Length == 0) return "-";

            var textList = new List<String>();
            foreach (var s in status)
            {
                switch (s)
                {
                    case ConfigStatus.Disabled:
                        textList.Add(chinese ? "未启用" : "Disabled");
                        break;
                    case ConfigStatus.Enabled:
                        textList.Add(chinese ? "正常" : "OK");
                        break;
                    case ConfigStatus.EnabledWithError:
                        textList.Add(chinese ? "错误" : "Error");
                        break;
                    case ConfigStatus.EnabledWithWarning:
                        textList.Add(chinese ? "警告" : "Warning");
                        break;
                    default:
                        textList.Add(chinese ? "未知" : "Unknown");
                        break;
                }
            }

            return String.Join(", ", textList);
        }

        private void importConfig(String filePath, String id)
        {
            var xml = new XmlDocument();
            try { xml.Load(filePath); }
            catch (Exception)
            {
                Agency.PopupError(chinese ? "无法打开配置文件。" : "Failed to load configuration file.");
                return;
            }

            var rootNode = xml.DocumentElement;
            var typeName = rootNode.Attributes["type"].Value.ToLower();

            if (typeName == id)
            {
                Agency.SetModuleConfig(caller, id, Encoding.UTF8.GetString(Convert.FromBase64String(rootNode.InnerText)));
            }
            else
            {
                Agency.PopupError(chinese ? "配置文件类型不匹配。" : "The type of configuration file dismatched.");
            }
        }

        private void exportConfig(String filePath, String id)
        {
            var config = Agency.GetModuleConfig(caller, id);
            if (config == null) config = "";

            var xml = Xml.Create("root");
            var root = xml.DocumentElement;
            root.Attributes.Append(xml.CreateAttribute("type")).Value = id;
            root.InnerText = Convert.ToBase64String(Encoding.UTF8.GetBytes(config));
            xml.Save(filePath);

            Agency.Log(filePath + (chinese ? "已保存" : " saved."), LogLevel.Info);
        }

        private bool checkIsXml(String str, out String niceXmlString)
        {
            niceXmlString = "";
            if (str == null) str = "";
            if (str.StartsWith("<?xml"))
            {
                var xml = new XmlDocument();
                try
                {
                    xml.LoadXml(str);
                    niceXmlString = toNiceString(xml);
                    return true;
                }
                catch (Exception) {}
            }
            return false;
        }

        private String toNiceString(XmlDocument xml)
        {
            if (xml == null) return "";

            var memoryStream = new MemoryStream();

            var settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.IndentChars = "    ";

            var xmlWriter = XmlWriter.Create(memoryStream, settings);
            xml.Save(xmlWriter);
            xmlWriter.Close();

            memoryStream.Position = 3; // BOM
            var data = new byte[memoryStream.Length - 3];
            memoryStream.Read(data, 0, (int)memoryStream.Length - 3);
            memoryStream.Close();

            return Encoding.UTF8.GetString(data);
        }

        private ListBox list;

        private bool chinese = Agency.GetAppLanguage() == "ch";
        private object caller;
    }

    class ConfigEditorItem : IComparable
    {
        public String ID { get; set; }
        public String Name { get; set; }

        public int CompareTo(object obj)
        {
            return Name.CompareTo((obj as ConfigEditorItem).Name);
        }

        public override string ToString()
        {
            return Name;
        }
    }
}